// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
/*
 * libfdt - Flat Device Tree manipulation
 * Copyright (C) 2006 David Gibson, IBM Corporation.
 */
#include "libfdt_env.h"

#include <fdt.h>
#include <libfdt.h>

#include "libfdt_internal.h"

int fdt_check_full(const void *fdt, size_t bufsize)
{
    int err;
    int num_memrsv;
    int offset, nextoffset = 0;
    uint32_t tag;
    unsigned int depth = 0;
    const void *prop;
    const char *propname;
    bool expect_end = false;

    if (bufsize < FDT_V1_SIZE)
        return -FDT_ERR_TRUNCATED;
    if (bufsize < fdt_header_size(fdt))
        return -FDT_ERR_TRUNCATED;
    err = fdt_check_header(fdt);
    if (err != 0)
        return err;
    if (bufsize < fdt_totalsize(fdt))
        return -FDT_ERR_TRUNCATED;

    num_memrsv = fdt_num_mem_rsv(fdt);
    if (num_memrsv < 0)
        return num_memrsv;

    while (1) {
        offset = nextoffset;
        tag = fdt_next_tag(fdt, offset, &nextoffset);

        if (nextoffset < 0)
            return nextoffset;

        /* If we see two root nodes, something is wrong */
        if (expect_end && tag != FDT_END)
            return -FDT_ERR_BADSTRUCTURE;

        switch (tag) {
        case FDT_NOP:
            break;

        case FDT_END:
            if (depth != 0)
                return -FDT_ERR_BADSTRUCTURE;
            return 0;

        case FDT_BEGIN_NODE:
            depth++;
            if (depth > INT_MAX)
                return -FDT_ERR_BADSTRUCTURE;

            /* The root node must have an empty name */
            if (depth == 1) {
                const char *name;
                int len;

                name = fdt_get_name(fdt, offset, &len);
                if (!name)
                    return len;

                if (*name || len)
                    return -FDT_ERR_BADSTRUCTURE;
            }
            break;

        case FDT_END_NODE:
            if (depth == 0)
                return -FDT_ERR_BADSTRUCTURE;
            depth--;
            if (depth == 0)
                expect_end = true;
            break;

        case FDT_PROP:
            prop = fdt_getprop_by_offset(fdt, offset, &propname,
                             &err);
            if (!prop)
                return err;
            break;

        default:
            return -FDT_ERR_INTERNAL;
        }
    }
}
